package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	"io"
	"time"
)

const (
	XRequestIDKey = "X-Request-ID"
)

// RequestID 是一个中间件，它会将一个 'X-Request-ID' 注入到每个请求的上下文以及请求/响应头中。
func RequestID() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 检查请求头中是否已经存在 'X-Request-ID'
		requestID := ctx.GetHeader(XRequestIDKey)
		if requestID == "" {
			// 如果不存在，生成一个新的 UUID 作为请求 ID
			requestID = uuid.Must(uuid.NewV4()).String()
			// 将请求 ID 注入到请求头中
			ctx.Request.Header.Set(XRequestIDKey, requestID)
			// 将请求 ID 存储到上下文里，方便后续使用
			ctx.Set(XRequestIDKey, requestID)
		}
		// 将请求 ID 注入到响应头中
		ctx.Writer.Header().Set(XRequestIDKey, requestID)
		// 继续处理请求
		ctx.Next()
	}
}

// GetLoggerConfig 返回一个 gin.LoggerConfig，它会将日志写入指定的 io.Writer 并使用给定的 gin.LogFormatter。
// 默认情况下，gin.DefaultWriter = os.Stdout
// 参考：https://github.com/gin-gonic/gin#custom-log-format
func GetLoggerConfig(formatter gin.LogFormatter, output io.Writer, skipPaths []string) gin.LoggerConfig {
	if formatter == nil {
		// 如果没有指定日志格式化器，则使用默认的带有请求 ID 的日志格式化器
		formatter = GetDefaultLogFormatterWithRequestID()
	}

	return gin.LoggerConfig{
		Formatter: formatter,
		Output:    output,
		SkipPaths: skipPaths,
	}
}

// GetDefaultLogFormatterWithRequestID 返回一个带有 'RequestID' 的 gin.LogFormatter。
func GetDefaultLogFormatterWithRequestID() gin.LogFormatter {
	return func(param gin.LogFormatterParams) string {
		var statusColor, methodColor, resetColor string
		if param.IsOutputColor() {
			// 获取状态码的颜色
			statusColor = param.StatusCodeColor()
			// 获取请求方法的颜色
			methodColor = param.MethodColor()
			// 获取重置颜色的字符串
			resetColor = param.ResetColor()
		}

		if param.Latency > time.Minute {
			// 如果请求处理时间超过一分钟，以一种在 Go 1.8 之前的版本也安全的方式截断时间
			param.Latency -= param.Latency % time.Second
		}

		return fmt.Sprintf("%s%3d%s - [%s] \"%v %s%s%s %s\" %s",
			// param.TimeStamp.Format("2006/01/02 - 15:04:05"),
			statusColor, param.StatusCode, resetColor,
			param.ClientIP,
			param.Latency,
			methodColor, param.Method, resetColor,
			param.Path,
			param.ErrorMessage,
		)
	}
}

// GetRequestIDFromContext 从给定的上下文中获取 'RequestID'，如果存在的话。
func GetRequestIDFromContext(c *gin.Context) string {
	if v, ok := c.Get(XRequestIDKey); ok {
		// 尝试将获取的值转换为字符串类型
		if requestID, ok := v.(string); ok {
			return requestID
		}
	}

	return ""
}
